package com.appspot.moviesessions.infrastructure.scraper;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

import com.appspot.moviesessions.model.Movie.Fields;
import com.appspot.moviesessions.model.MovieInformationFoundEvent;
import com.appspot.moviesessions.model.MoviePosterFoundEvent;

@Component("movieInformationScraper")
public class IMDBApiScraper implements Scraper {

	private URLFetchService urlFetchService;
	private ApplicationEventPublisher eventPublisher;
	private static final String URL_FORMAT = "http://www.imdbapi.com/?i=&t=%s";
	private static final Log LOG = LogFactory.getLog(IMDBApiScraper.class);

	@Autowired
	public IMDBApiScraper(URLFetchService urlFetchService,
			ApplicationEventPublisher eventPublisher) {
		this.urlFetchService = urlFetchService;
		this.eventPublisher = eventPublisher;
	}

	@Override
	public void scrape(Map<String, String> parameters) {
		String movieName = parameters.get("movie");
		String response = new String(
				urlFetchService.fetch(toMovieUrl(movieName)),
				Charset.forName("UTF-8"));
		Map<String, Object> data = toMap(response);
		Map<Fields, Object> updateFields = new HashMap<Fields, Object>();
		for (String key : data.keySet()) {
			Fields field = Fields.match(key.toUpperCase());
			if (field != null) {
				updateFields.put(field, data.get(key));
			}
		}
		if (updateFields.keySet().size() > 0) {
			eventPublisher.publishEvent(new MovieInformationFoundEvent(this,
					movieName, updateFields));
		}

		String posterUrl = String.valueOf(updateFields.get(Fields.POSTER));

		eventPublisher.publishEvent(new MoviePosterFoundEvent(this, movieName,
				fetchPoster(posterUrl)));
	}

	private boolean isValidUrl(String posterUrl) {
		return (posterUrl == null) ? false : posterUrl.matches("http.*");
	}

	private byte[] fetchPoster(String url) {
		if (isValidUrl(url)) {
			return urlFetchService.fetch(toURL(url));
		}
		try {
			return IOUtils.toByteArray(getClass().getClassLoader()
					.getResourceAsStream("default-poster.jpg"));
		} catch (IOException e) {
			throw new RuntimeException(e);
		}

	}

	private Map<String, Object> toMap(String response) {
		ObjectMapper mapper = new ObjectMapper();
		try {
			return mapper.readValue(response, Map.class);
		} catch (Exception e) {
			LOG.error(e);
			return Collections.emptyMap();
		}
	}

	private URL toMovieUrl(String movie) {
		return toURL(String.format(URL_FORMAT, encode(movie)));
	}

	private URL toURL(String url) {
		try {
			return new URL(url);
		} catch (MalformedURLException e) {
			throw new RuntimeException(e);
		}
	}

	private String encode(String str) {
		try {
			return URLEncoder.encode(str, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			return str;
		}
	}

}
